home *** CD-ROM | disk | FTP | other *** search
/ MacFormat UK 172 / MF_UK_172_1.iso / DiscContents / Software / eBay toolkit / Easy eBay 0.4 / Easy eBay / easyebay-0.4.wdgt / Scroller.js < prev    next >
Encoding:
JavaScript  |  2005-06-08  |  15.2 KB  |  381 lines

  1. /*
  2.  
  3. Copyright _ 2005, Apple Computer, Inc.  All rights reserved.
  4. NOTE:  Use of this source code is subject to the terms of the Software
  5. License Agreement for Mac OS X, which accompanies the code.  Your use
  6. of this source code signifies your agreement to such license terms and
  7. conditions.  Except as expressly granted in the Software License Agreement
  8. for Mac OS X, no other copyright, patent, or other intellectual property
  9. license or right is granted, either expressly or by implication, by Apple.
  10.  
  11. */
  12.  
  13.  
  14. /*
  15.  ****************************************
  16.  * Objects used by the Scroller    *
  17.  ****************************************
  18.  */    
  19.     var currentContent;            // currently-visible DIV.  Necessary for the Scroller to be shared across DIVs
  20.     var currentContentStyle;    // style object of the currentContent
  21.     var scrollBar;                // Parent scrollbar DIV.  Contains the track and thumb
  22.     var scrollThumb;            // Scroller's thumb control
  23.     var scrollTrack;            // Scroller's base/track
  24.     var trackTimer;                // for extended mousedowns in the scroll track
  25.  
  26. /*
  27.  ********************************************
  28.  *  Dimensions used by the Scroller    *
  29.  ********************************************
  30.  */
  31.     var currentContentHeight;                // height of currently-visible content DIV
  32.     var currentContentTop;                    // top....
  33.     var viewHeight;                            // height of the parent (overflow:hidden) view
  34.     var trackMouseY;                        // mouse location in the scroll track
  35.     var thumbHeight;                        // height of the thumb control
  36.     var tracking                    = true;    // is the mouse down in the scroll track
  37.     var thumbStartY                    = -1;    // point where we started scrolling with the thumb
  38.     var scrollThumbStartPos            = -1;    // thumb's 'top' value when we started scrolling
  39.     var scrollBarHeight;                    // height of our scrollbar.  TBD    
  40.     var numberOfScrollablePixels;            // for calculating thumb size/position and content position ("page number")
  41.  
  42. /*
  43.  ********************************************************
  44.  * Constants.  Hardcoded to match respective CSS values    *
  45.  ********************************************************
  46.  */
  47.     var SCROLLBAR_TOP        = -1;
  48.     var SCROLL_THUMB_HEIGHT    = 27;
  49.     
  50.     // CSS element names of critical DIVs.  Abstracted for easy customization.
  51.     var SCROLLBAR_DIV_NAME        = 'myScrollBar';        // Name of the parent scroller div, containing the track and the thumb.
  52.     var SCROLLTHUMB_DIV_NAME    = 'myScrollThumb';        // Div containing the scroller thumb
  53.     var SCROLLTRACK_DIV_NAME    = 'myScrollTrack';         // Div containing the scroller track
  54.     var TRACK_TOP_DIV_NAME        = 'myScrollTrackTop';    // Top edge of scroller track
  55.     var TRACK_MID_DIV_NAME        = 'myScrollTrackMid';    // variable-size center of scroller track
  56.     var TRACK_BOT_DIV_NAME        = 'myScrollTrackBot';    // Bottom edge of scroller track
  57.     
  58.     var PAGE_SKIP_PAUSE    = 150; // time (ms) between page jumps when holding the mouse down in the track.
  59.     
  60. // Calculate the height of the views and make the thumb proportional.
  61. // If a single scroller is being shared across multiple divs (as in this sample),
  62. // this function must be called whenever the divs swap, to recalibrate the scrollbar.
  63. function calculateAndShowThumb(contentDiv, resetTop) {
  64.     
  65.  
  66.     scrollBar    = document.getElementById(SCROLLBAR_DIV_NAME);
  67.     scrollThumb = document.getElementById(SCROLLTHUMB_DIV_NAME);
  68.     scrollTrack = document.getElementById(SCROLLTRACK_DIV_NAME);
  69.     
  70.     
  71.     
  72.     if (contentDiv != null) {
  73.         currentContent = contentDiv;
  74.     } else if (currentContent == null) {
  75.         hideScrollbar();
  76.     }
  77.     
  78.     // currentContent.style.display = 'block';
  79.     currentContentStyle = document.defaultView.getComputedStyle(currentContent,'');
  80.         DEBUG("cast: cct=" + currentContentStyle.getPropertyValue('top'));
  81.     currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
  82.     currentContentHeight = parseInt(currentContentStyle.getPropertyValue('height'));
  83.     
  84.     viewHeight = parseFloat (document.defaultView.getComputedStyle(currentContent.parentNode, '').getPropertyValue('height'));
  85.         DEBUG("cast: viewHeight=" + viewHeight);
  86.     scrollBarHeight = parseInt(document.defaultView.getComputedStyle(scrollBar, '').getPropertyValue('height'));
  87.         DEBUG("cast: currentContentHeight=" + currentContentHeight + " top=" + currentContentTop + " scrollBarHeight=" + scrollBarHeight);
  88.  
  89.     var percent = getProportion (viewHeight, currentContentHeight);
  90.         DEBUG("cast: percent=" + percent);
  91.     
  92.     // hide the scrollbar if all the content is showing.  Determined by the calculated scrollbar height and position.
  93.     if (percent == 0) {
  94.             DEBUG("cast: 0% thumbHeight.  Hiding scrollbar");
  95.         hideScrollBar();
  96.     } else {
  97.         // Position the thumb according to where the content is currently scrolled.
  98.         // This is necessary for sharing the same scrollbar between multiple content
  99.         // panes that will likely be at different scroll positions.
  100.         thumbHeight = Math.max(Math.round(scrollBarHeight * percent), SCROLL_THUMB_HEIGHT);
  101.         if ( resetTop == true ) {
  102.             thumbTop = -1;
  103.         }
  104.         else {
  105.             thumbTop = thumbPositionForPagePosition(currentContentTop);
  106.         }
  107.         
  108.         scrollThumb.style.height = thumbHeight + 'px';
  109.         scrollThumb.style.top = thumbTop;
  110.         
  111.         numberOfScrollablePixels = scrollBarHeight - thumbHeight - SCROLLBAR_TOP;
  112.             DEBUG("cast: new thumb height=" + scrollThumb.style.height);
  113.         
  114.         // This is a safeguard so the content matches the new thumb position.  Necessary for live-resizing to work.
  115.         scrollContent(thumbTop);
  116.         
  117.         showScrollBar();
  118.     }
  119. }
  120.  
  121. // Hide the thumb and track, but keep the parent scrollbar DIV around to preserve formatting
  122. function hideScrollBar() {
  123.     scrollTrack.style.display = 'none';
  124.     scrollThumb.style.display = 'none';
  125. }
  126.  
  127. function showScrollBar() {
  128.     scrollTrack.style.display = 'block';
  129.     scrollThumb.style.display = 'block';
  130. }
  131.  
  132. /*
  133.  ********************************
  134.  *    Thumb Scrolling Functions    *
  135.  ********************************
  136.  */
  137.  
  138. // This mouseDown is presumably the start of a thumb drag (scroll) action.
  139. function mouseDownScrollThumb (event) {
  140.     // We add these listeners and remove them later; they're only useful while there is mouse activity
  141.     // on the thumb.  This is necessary because there is no mousedrag event in JavaScript.
  142.     document.addEventListener("mousemove", mouseMoveScrollThumb, true);
  143.     document.addEventListener("mouseup", mouseUpScrollThumb, true);
  144.     
  145.     thumbStartY = event.y;
  146.  
  147.     scrollThumbStartPos = parseInt(document.defaultView.getComputedStyle(scrollThumb,'').getPropertyValue('top'));
  148.  
  149.         DEBUG("mdThumbHeight:" + thumbHeight + " thumbStartY=" + thumbStartY);
  150. }
  151.  
  152. // At this point we are dragging the scrollThumb.  We know this because the mousemove listener is only installed
  153. // after a mousedown.
  154. function mouseMoveScrollThumb (event) {
  155.     var deltaY = event.y - thumbStartY;
  156.     
  157.     var newPosition = scrollThumbStartPos + deltaY;
  158.         DEBUG("mmst: event.y=" + event.y + " thumbStartY=" + thumbStartY + " thumbStart=" + scrollThumbStartPos);
  159.     scrollContent(newPosition);
  160. }
  161.  
  162. function scrollContent(newThumbPosition) {
  163.         DEBUG("scrollContent: newPositionIn=" + newThumbPosition);
  164.  
  165.     // Correct if we're going to clip above the top or below the bottom
  166.     if (newThumbPosition < SCROLLBAR_TOP) {
  167.             DEBUG("sc: thumb too high (" + newThumbPosition + ")");
  168.         newThumbPosition = SCROLLBAR_TOP;
  169.     } else if ((newThumbPosition + thumbHeight) > scrollBarHeight) {
  170.             DEBUG("sc: thumb too low (" + newThumbPosition + ")");
  171.         newThumbPosition = scrollBarHeight - thumbHeight;
  172.     }
  173.         
  174.         DEBUG("scrollContent: newPosition=" + newThumbPosition);
  175.     scrollThumb.style.top = newThumbPosition + 'px';
  176.     
  177.         DEBUG("calculating delta");
  178.     currentContentTop = pagePositionForThumbPosition(newThumbPosition);
  179.         DEBUG("scrollContent: thumbTop=" + scrollThumb.style.top + " currentContentTop is " + currentContentTop);
  180.     currentContent.style.top = currentContentTop + 'px';
  181. }
  182.  
  183. function mouseUpScrollThumb (event) {
  184.         DEBUG("must: eventY=" + event.y);
  185.     // After mouseup, these events are just noise. Remove them; they'll be re-added on the next mouseDown
  186.     document.removeEventListener("mousemove", mouseMoveScrollThumb, true);
  187.     document.removeEventListener("mouseup", mouseUpScrollThumb, true);
  188.     
  189.     // reset the starting position
  190.     thumbStartY = -1;
  191. }
  192.  
  193. /*
  194.  ********************************
  195.  *    Track Scrolling Functions    *
  196.  ********************************
  197.  */
  198.  
  199. function mouseDownTrack (event) {
  200.     updateTrackMouseY(event);
  201.     
  202.     scrollTrack.addEventListener("mousemove", mouseMoveTrack, false);
  203.     scrollTrack.addEventListener("mouseover", mouseOverTrack, false);
  204.     scrollTrack.addEventListener("mouseout", mouseOutTrack, false);
  205.     
  206.     // This is our handling for clicks in the track.
  207.     var thumbTop = document.defaultView.getComputedStyle(scrollThumb,'').getPropertyValue('top');
  208.         DEBUG("trackClick: mouseY=" + trackMouseY + " scrollThumbY=" + thumbTop);
  209.     if (trackMouseY > parseInt(thumbTop)) {
  210.             DEBUG("click BELOW thumb ");
  211.         pageDown();
  212.         trackTimer = setInterval("pageDown();", PAGE_SKIP_PAUSE);
  213.     } else {
  214.             DEBUG("click ABOVE thumb");
  215.         pageUp();
  216.         trackTimer = setInterval("pageUp();", PAGE_SKIP_PAUSE);
  217.     }
  218.     
  219.     DEBUG("mdt: newPosition=" + trackMouseY);
  220. }
  221.  
  222. function mouseMoveTrack(event) {
  223.     // If the mouse moved while being held down, update the location so we 
  224.     // stop the track-scrolling in the right place.
  225.     updateTrackMouseY(event);
  226. }
  227.  
  228. function mouseOutTrack(event) {
  229.     // When the mouse moves out while pressed, we turn track-scrolling off.
  230.     // The timer keeps firing, but pageUp/pageDown exits based on this value
  231.     tracking = false;
  232. }
  233.  
  234. function mouseOverTrack(event) {
  235.     // The timer is still firing, but pageUp/pageDown are waiting for the mouse to
  236.     // return to the track.  This will resume track-scrolling.
  237.     tracking = true;
  238. }
  239.  
  240. function mouseUpTrack(event) {
  241.     // stop track-scrolling
  242.     clearInterval(trackTimer);
  243.     
  244.     // After mouseup, these events are just noise. Remove them; they'll be re-added on the next mouseDown
  245.     scrollTrack.removeEventListener("mousemove", mouseMoveTrack, false);
  246.     scrollTrack.removeEventListener("mouseover", mouseMoveTrack, false);
  247.     scrollTrack.removeEventListener("mouseout", mouseMoveTrack, false);
  248. }
  249.  
  250. // correct the coordinates for the sourceEvent so they properly match the source component
  251. // **YOU MAY NEED TO UPDATE THIS FUNCTION** depending on how deeply the scrollbar div is nested
  252. function updateTrackMouseY (event) {
  253.         DEBUG("utmY: source=" + event.toElement.id + " offsetY=" + event.offsetY + " layerY=" + event.layerY + " offsetTop=" + event.toElement.offsetTop);
  254.     
  255.     if (event.toElement.id == 'myScrollTrackMid') {
  256.         // source is the ctr component of the track; offset by the top component.
  257.         var topHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_TOP_DIV_NAME)).getPropertyValue('height');
  258.         trackMouseY = event.offsetY + parseInt(topHeight);
  259.             DEBUG("utmY: click in mid, topHeight=" + topHeight);
  260.     } else if (event.toElement.id == TRACK_BOT_DIV_NAME) {
  261.         // source is the bottom component of the track; offset by the top and the middle.
  262.         var midHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_MID_DIV_NAME)).getPropertyValue('height');
  263.         var topHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_TOP_DIV_NAME)).getPropertyValue('height');
  264.         trackMouseY = event.offsetY + parseInt(midHeight) + parseInt(topHeight);
  265.             DEBUG("utmY: click in bottom, offsetY=" + event.offsetY + " offsetTop=" + event.toElement.offsetTop + " mid+top=" + (parseInt(midHeight) + parseInt(topHeight)));
  266.     } else {
  267.             DEBUG("utmY: click in top, offsetY=" + event.offsetY + " offsetTop=" + event.toElement.offsetTop + " parentTop=" + event.toElement.parentNode.offsetTop);
  268.         // source is the top of the track
  269.         trackMouseY = event.offsetY - (event.toElement.offsetTop + event.toElement.parentNode.offsetTop);
  270.     }    
  271.     
  272.         DEBUG("utmY: trackMouseY" + trackMouseY);
  273. }
  274.  
  275. /*
  276.  ********************************************************************
  277.  * pageUp/pageDown                                                    *
  278.  * Used by track-scrolling code, but can be called independently    *
  279.  ********************************************************************
  280.  */ 
  281.  
  282. // Reposition the content one page (viewHeight) upwards.  Prevent out-of-bounds values.
  283. // Remember that the content top becomes increasingly NEGATIVE (moves upwards) as we scroll down.    
  284. function pageDown() {
  285.     if (!tracking) return;
  286.     // calculate the last page.  This is equal to the content's full height, less one viewHeight
  287.     // Again, the value is negative because that's how far offset the content would need to be.
  288.     currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
  289.     var lastPageY = -(currentContentHeight - viewHeight);
  290.     // calculate the next page from the content's current position.
  291.     var nextPageY = currentContentTop - viewHeight;
  292.         DEBUG("pageDown: currentContent.lastPageY=" + lastPageY + " nextPageY=" + nextPageY);
  293.     currentContentTop = Math.max(lastPageY, nextPageY);
  294.     currentContent.style.top = currentContentTop + 'px';
  295.  
  296.     // reposition the scroll thumb based on the new page position.
  297.     var newThumbTop = thumbPositionForPagePosition(currentContentTop);
  298.     var thumbBottom = newThumbTop + parseInt(scrollThumb.style.height);
  299.     scrollThumb.style.top = newThumbTop;
  300.  
  301.         DEBUG("pageDown: currentContentTop=" + currentContentTop + " currentContentHeight=" + currentContentHeight + " newThumbTop=" + newThumbTop + " thumbBottom=" + thumbBottom);
  302.  
  303.     if (trackMouseY < thumbBottom) {
  304.         // the thumb has met the mouse; time to stop track-scrolling.
  305.         clearInterval(trackTimer);
  306.     }
  307. }
  308.  
  309. // very similar to pageDown, with some values negated to move the content in a different direction.
  310. function pageUp() {
  311.     if (!tracking) return;
  312.     currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
  313.     var firstPageY = 0;
  314.     var nextPageY = currentContentTop + viewHeight;
  315.     currentContentTop = Math.min(firstPageY, nextPageY)
  316.     currentContent.style.top = currentContentTop + 'px';
  317.     
  318.     var newThumbTop = thumbPositionForPagePosition(currentContentTop);
  319.         DEBUG("pageUp: contentTop=" + currentContentTop + " newThumbTop=" + newThumbTop);// + " thumbBottom=" + thumbBottom);
  320.     scrollThumb.style.top = newThumbTop;
  321.  
  322.     if (trackMouseY > newThumbTop) {
  323.         clearInterval(trackTimer);
  324.     }
  325. }
  326.  
  327. /*
  328.  ********************************
  329.  *    Utility / Math functions    *
  330.  ********************************
  331.  */
  332.  
  333. function getProportion (viewheight, documentheight) {
  334.     if (documentheight <= viewheight)
  335.         return 0;
  336.     else
  337.         return viewheight/documentheight;
  338. }
  339.  
  340. // Given the position of the thumb, tell us what the content top should be.
  341. // This is the key value that allows us to thumb-scroll.
  342. function pagePositionForThumbPosition (thumbPosition) {
  343.     return -(thumbPosition - SCROLLBAR_TOP) * ((currentContentHeight - viewHeight) / numberOfScrollablePixels);
  344. }
  345.  
  346. // Given the position of the page, tell us where the thumb should be.
  347. // This is the key value that allows us to track-scroll.
  348. function thumbPositionForPagePosition (pagePosition) {
  349.     return -(pagePosition / ((currentContentHeight - viewHeight) / numberOfScrollablePixels)) + SCROLLBAR_TOP;
  350. }
  351.  
  352. /*
  353.  ****************************
  354.  *    END SCROLLER FUNCTIONS    *
  355.  ****************************
  356.  */
  357.  
  358. // debug code uses the div defined in Scroller.html demo
  359.  
  360. var debugMode = false;
  361.  
  362. // write to the debug div.
  363. function DEBUG(str) {
  364.     if (debugMode) {
  365.         if (window.widget) {
  366.             alert(str);
  367.         } else {
  368.             document.getElementById('debugDiv').innerHTML += str + "<br>";
  369.         }
  370.     }
  371. }
  372.  
  373. // toggle the debugMode flag, but only show the debugDiv if we're in Safari
  374. function toggleDebug() {
  375.     debugMode = !debugMode;
  376.     if (debugMode == true && !window.widget) {
  377.         document.getElementById('debugDiv').style.display = 'block';
  378.     } else {
  379.         document.getElementById('debugDiv').style.display = 'none';
  380.     }
  381. }